Raku原本的名字是Perl6,於2019年10月正式更名為Perl6,
主要原因是因為作者認為語法上跟上一代的Perl5差異太大,因此獨立出來成為一門新的語言
但是perl7還是會推出,所以實際上應該會變成兩個獨立的版本,概念上類似python2跟python3
Perl跟python一樣內建在某些版本的linux(Mac不清楚,但聽說有)
如果跟我一樣使用linux的可以嘗試看看下面的程式碼吧
perl -e 'print "Hello world!\n"'
應該會跟當初的我一樣嚇到
阿我的電腦裡面怎麼有這種東西
不過我們今天要學的是新的Raku,目前還沒有流行到會預設裝在系統裡面,因此等一下我們依舊會使用docker
雖然前面提到很多次python跟Perl的類似(甚至他們都是名次為P開頭的語言)
但實際上兩者的開發理念是完全相反的
python的哲學是
There should be one– and preferably only one –obvious way to do it
只有一種方法,而且最好只有一種來做事
而Perl則是
There is more than one way to do it
不只一種方法來做一件事
因此兩者實際上在理念是有差異的
寫起來的感覺上,python更像在做程式開發,而Raku則是像在寫文章
先去DockerHub找Raku,然後我們開始吧
等一下,我找不到Raku
喔...我發現了rakudo,是這個嗎?
rakudo是Raku這門語言的編譯器實現
可以將按照Raku語法的檔案轉譯成機器語言,使電腦可以執行
所以去抓rakudo-star的image吧
先建立一個附檔名為p6的檔案(或是.raku,.pl6都可以)
接著貼上下面的程式
sub t2h($input){
loop (my $i = 0; $i < 16; $i++) {
loop (my $j = 0 ;$j < 16;$j++){
loop (my $k = 0;$k < 16;$k++){
return sprintf "%s%s%s", returnAE($i) , returnAE($j), returnAE($k) if (16**2*$i+16**1*$j+16**0*$k==$input)
}
}
}
}
sub returnAE($input) {
return $input if $input < 10;
given $input {
when 10 { return "A"; }
when 11 { return "B"; }
when 12 { return "C"; }
when 13 { return "D"; }
when 14 { return "E"; }
when 15 { return "F"; }
}
}
######################################################
sub h2t($input){
my @arr = $input.split("",:skip-empty);
my $output = 0;
my $i = 0;
while $i < @arr.elems {
$output += AEreturn(@arr[$i])*16**(@arr.elems-$i-1);
$i++;
}
$output ;
}
sub AEreturn($input){
given $input {
when "A" { return 10; }
when "B" { return 11; }
when "C" { return 12; }
when "D" { return 13; }
when "E" { return 14; }
when "F" { return 15; }
}
$input;
}
##############################
my $choose;
say "Tell me what you want to do:";
say "(1)T to H (2)H to T";
$choose = get;
given $choose {
when "1" {
say "Please enter the number";
my $number = get;
t2h($number).say;
}
when "2" {
say "Please enter the number";
my $number = get;
h2t($number).say;
}
default {
die "Wrong select";
}
}
使用
docker container run --rm -it -v $PWD:/home/raku rakudo-star:alpine raku /home/raku/convert.p6
就可以執行raku檔拉
注意Raku是很要求風格的語言,有時候在某些地方多了或少了空格程式就跑不起來
如果你有遇到某些狀況,請檢查一下程式碼
跟大部分好寫的語言一樣,Raku是直譯式語言,所以程式進入點就在程式碼的第一行
與Ruby不同點在於Raku需要在行尾使用;
註解使用#
如果你需要多行註解可以這樣寫
=begin pd
This is a multi line comment.
Comment 1
Comment 2
=end pd
跟之前一樣先往下看到程式本體吧
my $choose;
這個是Raku的變數宣告
還記得以前使用過的var吧
my是類似的東西
另外雖然php的變數都也都使用$開頭
但是在Raku實際有好幾種,以下是常見的三種
$choose #表示一般變數
@choose #表示陣列
%choose #表示雜湊表(目前可以先不管他,這次不會用到)
所以雖然Raku屬於動態型別,不必確實宣告變數的型別,但是還是要告訴Raku變數的種類
上一行宣告出變數之後我們就可以使用他來接輸入了
say "Tell me what you want to do:";
say "(1)T to H (2)H to T";
$choose = get;
是不是跟Ruby一樣簡單呢?
接下來我們看到Raku的switch
given $choose {
when "1" {
say "Please enter the number";
my $number = get;
t2h($number).say;
}
when "2" {
say "Please enter the number";
my $number = get;
h2t($number).say;
}
default {
die "Wrong select";
}
}
Ruby使用Case...when
而Raku使用given...when
跟Ruby一樣不需要break,但是需要{}
注意到這裡
t2h($number).say;
這是另外一種印出回傳值的方式,直接在後面加.say
當然,你也可以像往常一樣這樣印
say t2h($number);
default裡頭的die則是Raku拋出異常的寫法
你可以嘗試故意輸入錯誤,看看Raku的錯誤是怎麼拋出的
往上看到副程式吧
sub t2h($input){
loop (my $i = 0; $i < 16; $i++) {
loop (my $j = 0 ;$j < 16;$j++){
loop (my $k = 0;$k < 16;$k++){
return sprintf "%s%s%s", returnAE($i) , returnAE($j), returnAE($k) if (16**2*$i+16**1*$j+16**0*$k==$input)
}
}
}
}
Raku的方法關鍵字用sub
輸入的部份由於需要命名變數名稱,因此記得加上$之類的前綴
與其他動態語言相同,不需要指定輸入與輸出的型別
loop (my $i = 0; $i < 16; $i++) {
Raku的迴圈使用loop,類似以往我們寫的for,裡面的東西也跟我們之前寫for迴圈時類似
記得有()跟{}
Raku有一種很特別的if寫法
return sprintf "%s%s%s", returnAE($i) , returnAE($j), returnAE($k) if (16**2*$i+16**1*$j+16**0*$k==$input)
他可以把return寫在前面,if在後面
這種寫法更像是在寫文章而不是在寫程式,尤其是if裡面只有一個return時是最方便的
當然,你也可以這樣寫
if 16**2*$i+16**1*$j+16**0*$k==$input {
return sprintf "%s%s%s", returnAE($i) , returnAE($j), returnAE($k)
}
if的()可加可不加
注意到if裡面計算冪次的方式
if (16**2*$i+16**1*$j+16**0*$k==$input)
跟Ruby相同使用**作為次方的運算
Raku採用弱型別系統,因此我們不需要將輸入的值$input給轉型就可以跟數值做比較
這點跟Ruby就不同了,Ruby雖然是動態型別,但採用強型別系統,字串沒做轉型之前沒辦法跟數值做比較
因此你可以看到這樣寫
if (16**2*$i+16**1*$j+16**0*$k==$input)
讓我們看到另外一支將十六進位轉成十進位的方法,看看Raku是怎麼處理字串的
sub h2t($input){
my @arr = $input.split("",:skip-empty);
my $output = 0;
my $i = 0;
while $i < @arr.elems {
$output += AEreturn(@arr[$i])*16**(@arr.elems-$i-1);
$i++;
}
$output ;
}
在其他語言我們可以把字串當作字元陣列來使用,
但是在Raku由於是把一般變數跟陣列當作不同型別來看,因此我們這邊需要把字串分割成陣列
這裡使用.split這個方法,分割的基準採用""(也就是什麼都沒有)這樣就會把文字一個一個切出來
skip-empty則是捨棄分割時前後可能會產生的空字元,詳細的用法可以看這裡
這樣我們就將字串分割完了
接著我們把他丟入@arr這個變數裡(記得@是陣列的前綴,而my則是宣告的關鍵字吧)
你可以嘗試將程式碼更改如下
sub h2t($input){
my @arr = $input.split(""); #更改這一行
my $i = 0;
my $output = 0;
while $i < @arr.elems {
@arr[$i].say; # 印出每一個字元
$i++;
}
$output ;
}
看看如果沒有skip-empty的話印出來會長什麼樣子
我們來看看裡面的while吧
while $i < @arr.elems {
$output += AEreturn(@arr[$i])*16**(@arr.elems-$i-1);
$i++;
}
while跟ruby一樣條件不需要加(),但是需要{}
裡頭的
@arr.elems
則是取得陣列的長度,相當於以前的number.length
中間的算法基本跟以前一樣,我們往下看到最後一行
$output ;
這裡的return可加可不加
Raku預設會將方法的最後一行回傳
因此AEreturn的最後一行
$input;
也是相同意思
以上就是Raku的基本語法了
Raku的方法輸入值視為常數
sub add($input is copy) { #傳入的參數通常不能修改,因此要用is copy
$input += 1;
$input;
}
my $num = 10;
say "right" if add($num)==11
上面的方法如果沒有is copy會報錯
raku
Raku也有像是Ruby的irb可以使用指令模式
如果你點進去image的tag的話就可以看到他是怎麼寫dockerfile的了
有看到最後一行寫著
CMD ["raku"]
嗎?
raku其實就是Ruby的irb,使用指令模式的指令
試試看下面的docker命令吧
docker container run --rm -it rakudo-star:alpine
記得docker的tag要換成你們自己的
就可以開始玩Raku了
其實在介紹Ruby之前應該先介紹Raku的
這樣才有歷史傳承的感覺
但是寫著覺得這樣介紹起來比較順手,便這樣寫下去了
Raku算是寫起來比較開心的語言之一
雖然golang跟ruby寫起來也很舒服,但是
return $input if $input < 10;
#以及
@arr[$i].say;
這種語法寫起來太開心了
但是缺點也不是沒有,就是資源太少了
可能是因為還在Raku跟Perl6的名稱轉型期,因此同一筆搜尋可能要用perl6跟Raku兩種關鍵字去找
不過這點在未來應該會改善
另一個缺點則是直譯器的偵錯不太明顯
有時候只是行尾少了;沒注意到就報錯,而且還只是大概位置
比方說
when "1" {
say "Please enter the number" #把這裡的;拿掉
my $number = get;
t2h($number).say;
}
執行時會報錯
at /home/raku/convert.p6:62
------> say "Please enter the number"⏏<EOL
但實際上錯誤的是上一行
或是
when "1"{ #把"跟{中間的空格拿掉
會報錯
Missing block
at /home/raku/convert.p6:66
------> when ⏏"2"{
但是明明錯誤的不是第66行
直譯器的缺點未來應該都會改善,不然當專案越來越大時會越來越難找到問題點
(偏偏Raku還是直譯式語言,執行時才知道問題在哪)
因此我們來學一個編譯器超強的語言
而且還跟昨天今天一樣由R開頭
他就是鐵鏽
Rust
不過在那之前,我們來學型別吧
如果有任何寫不清楚或是觀念沒有很明白的話請留言告知我
會盡快補上
如果有任何寫錯的地方也麻煩留言告知我
會盡快修正
感謝各位